﻿Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.ComponentModel

#Region "Public Enums"

Public Enum MotionType As Integer
    SteadyAndAverage = 0
    SteadyAndSlow = 1
    SteadyAndFast = 2
    BellCurve = 3
    SlowlyIncreasing = 4
    SlowlyDecreasing = 5
    Increasing = 6
    Decreasing = 7
    Custom = 8
End Enum

Public Enum AlertLightStyle As Integer
    AlertLightOff = 0
    AlertLightOn = 1
    AlertLightBlinking = 2
End Enum

Public Enum GaugeFaceType
    White
    Black
    NewAge
End Enum

Public Enum RedlineStyle
    None = 0
    ThickRedLine = 1
    ThinRedLine = 2
    ThickVerticalLine = 3
    ThinVerticalLine = 4
End Enum
#End Region



<ToolboxBitmap(GetType(Unit), "toolbox1"), System.ComponentModel.DefaultEvent("ValueChanged")> _
Public Class Unit

    Private d As Double
    Private _hasfocus As Boolean = False

    Private dblTarget As Double = 1
    Private dblHalfWay As Double
    Private alertLightLastState As Boolean = False




#Region " Properties "

    <Browsable(False)> _
    Public Shared ReadOnly Property Version() As String
        Get
            Return My.Application.Info.Version.ToString

        End Get
    End Property


    Private _AlertLight As AlertLightStyle
    <Description("Indicates the state of the Red Alert Light"), _
    Category("Behavior"), DefaultValue(AlertLightStyle.AlertLightOff)> _
    Public Property AlertLight() As AlertLightStyle
        Get
            Return _AlertLight
        End Get
        Set(ByVal newValue As AlertLightStyle)
            If newValue <> _AlertLight Then
                Select Case newValue
                    Case AlertLightStyle.AlertLightOn
                        alertLightLastState = True
                    Case AlertLightStyle.AlertLightOff
                        alertLightLastState = False
                    Case AlertLightStyle.AlertLightBlinking
                        alertLightLastState = False
                End Select
                _AlertLight = newValue
                Me.Invalidate()
            End If

        End Set
    End Property


    Private _acceleration As Double
    <Description("The Acceleration, or change in dial's velocity when moving"), Category("Behavior")> <System.ComponentModel.DefaultValue(0)> _
    Public Property Acceleration() As Double
        Get
            Return _acceleration
        End Get
        Set(ByVal newValue As Double)
            'If newValue >= _maximum - _minimum Then
            'Err.Raise(vbObjectError + 110, "Acceleration", "Acceleration cannot be set higher than the difference between the Maximum Property and the Minimum Property")
            'ElseIf newValue <= -(_maximum - _minimum) Then
            'Err.Raise(vbObjectError + 111, "Acceleration", "Acceleration cannot be set lower than the negative difference between the Maximum Property and the Minimum Property")
            'Else
            _acceleration = newValue
            'End If

        End Set
    End Property


    Private _velocity As Double
    <Category("Behavior"), Description("The rate of change in the dials position")> <System.ComponentModel.DefaultValue(0)> _
    Public Property Velocity() As Double
        Get
            Return _velocity
        End Get
        Set(ByVal newValue As Double)
            If newValue >= _maximum - _minimum Then
                Err.Raise(vbObjectError + 120, "Velocity", "Velocity cannot be set higher than the difference between the Maximum Property and the Minimum Property")
            ElseIf newValue <= -(_maximum - _minimum) Then
                Err.Raise(vbObjectError + 121, "Velocity", "Velocity cannot be set lower than the negative difference between the Maximum Property and the Minimum Property")
            Else
                _velocity = newValue
            End If

        End Set
    End Property


    Private _value As Double
    <Category("Behavior"), Description("The value or position of the dial")> <System.ComponentModel.DefaultValue(0)> _
    Public Property Value() As Double
        Get
            Return _value
        End Get
        Set(ByVal newvalue As Double)
            If newvalue <> _value Then
                If (newvalue <= _maximum) And (newvalue >= _minimum) Then
                    'Reset Acceleration 

                    _value = newvalue
                    OnValueChanged(EventArgs.Empty)
                    If newvalue >= _redlineValue Then
                        OnRedLined(EventArgs.Empty)
                    Else
                        _isRedlined = False
                    End If
                Else
                    If Me.DataBindings.Count <= 0 Then
                        Err.Raise(vbObjectError + 130, , "Value", "Value must fall between Maximum and Minimum or it can either equal the minimum or the maximum (but not both)")
                    End If
                End If
            End If

        End Set
    End Property

    Private _minimum As Double
    <Category("Behavior"), Description("The lowest value of the dial. When the dial is at its lowest position its value will be this number.")> <System.ComponentModel.DefaultValue(0)> _
    Public Property Minimum() As Double
        Get
            Return _minimum
        End Get
        Set(ByVal newValue As Double)
            If newValue <> _minimum Then
                If newValue > _value Then
                    Err.Raise(vbObjectError + 100, "Minimum", "Minimum is too high. It must be less than or equal to the control's value.")
                ElseIf newValue >= _maximum Then
                    Err.Raise(vbObjectError + 101, "Minimum", "Minimum must be less than Maximum")
                Else
                    _minimum = newValue
                    OnMinimumChanged(EventArgs.Empty)
                    If _minimum > _redlineValue Then
                        RedlineValue = newValue
                    End If
                End If
            End If
        End Set
    End Property

    Private _maximum As Double
    <Category("Behavior"), Description("The highest value of the dial. When the dial is at its highest position its value will be this number.")> <System.ComponentModel.DefaultValue(200)> _
    Public Property Maximum() As Double
        Get
            Return _maximum
        End Get
        Set(ByVal newValue As Double)
            If newValue <> _maximum Then
                If newValue <= _minimum Then
                    Err.Raise(vbObjectError + 102, "Maximum", "Maximum must be higher than the Minimum")
                ElseIf newValue < _value Then
                    Err.Raise(vbObjectError + 103, "Maximum", "Maximum must be higher than the Control's Value")
                Else
                    _maximum = newValue
                    OnMaximumChanged(EventArgs.Empty)
                    If _maximum < _redlineValue Then
                        RedlineValue = newValue
                    End If
                End If


            End If

        End Set
    End Property

    Private _accelerationMethod As MotionType
    <Category("Behavior"), Description("The method, if any, the dials acceleration changes")> <System.ComponentModel.DefaultValue(MotionType.SteadyAndAverage)> _
    Public Property AccelerationMethod() As MotionType
        Get
            Return _accelerationMethod
        End Get
        Set(ByVal newValue As MotionType)
            If newValue <> _accelerationMethod Then
                _accelerationMethod = newValue
                AccelControl()

            End If

        End Set
    End Property

    Private _faceType As GaugeFaceType
    <Category("Appearance"), Description("How the background of the Guage appears")> <System.ComponentModel.DefaultValue(GaugeFaceType.Black)> _
    Public Property FaceType() As GaugeFaceType
        Get
            Return _faceType
        End Get
        Set(ByVal newValue As GaugeFaceType)
            If newValue <> _faceType Then
                _faceType = newValue
                OnFaceTypeChanged(EventArgs.Empty)
            End If

        End Set
    End Property

    Private _showValue As Boolean
    <System.ComponentModel.Category("Behavior"), System.ComponentModel.DefaultValue(True)> _
    Public Property ShowValue() As Boolean
        Get
            Return _showValue
        End Get
        Set(ByVal newValue As Boolean)
            If newValue <> _showValue Then
                _showValue = newValue
                If newValue Then
                    lblValue.Visible = True
                Else
                    lblValue.Visible = False
                End If
                Me.Invalidate()

            End If
        End Set
    End Property

    Private _redline As RedlineStyle
    <System.ComponentModel.Category("Appearance")> <System.ComponentModel.DefaultValue(CartersZoo.DisplayUnit.RedlineStyle.None)> _
    Public Property RedLineStyle() As RedlineStyle
        Get
            Return _redline
        End Get
        Set(ByVal newValue As RedlineStyle)
            If newValue <> _redline Then


                _redline = newValue
                Me.Invalidate()

            End If

        End Set
    End Property
    Private _redlineValue As Double
    <System.ComponentModel.Category("Behavior")> <System.ComponentModel.DefaultValue(90)> _
    Public Property RedlineValue() As Double
        Get
            Return _redlineValue
        End Get
        Set(ByVal newValue As Double)
            If newValue <> _redlineValue Then
                If newValue <= _maximum Then
                    If newValue >= _minimum Then
                        _redlineValue = newValue
                        If newValue >= _value Then
                            OnRedLined(EventArgs.Empty)
                        Else
                            _isRedlined = False
                        End If
                    End If
                End If
            End If
        End Set
    End Property

    Private _isRedlined As Boolean
    <DefaultValue(False), Browsable(False)> _
    Public ReadOnly Property IsRedlined() As Boolean
        Get
            Return _isRedlined
        End Get
    End Property


#End Region

#Region "Events"

    Private mOnValueChanged As EventHandler
    Private mOnMaximumChanged As EventHandler
    Private mOnMinimumChanged As EventHandler
    Private mOnFaceTypeChanged As EventHandler
    Private mOnRedLined As EventHandler
    Private mOnLostFocus As EventHandler
    Private mOnGotFocus As EventHandler


    Public Event ValueChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Event MaximumChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Event MinimumChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Event FaceTypeChanged(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Event RedLined(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Shadows Event LostFocus(ByVal sender As Object, ByVal e As System.EventArgs)
    Public Shadows Event GotFocus(ByVal sender As Object, ByVal e As System.EventArgs)
    'Event SomeEvent As EventHandler(Of CustomEventArgs)


    Protected Overridable Sub OnValueChanged(ByVal e As EventArgs)
        If Not (mOnValueChanged Is Nothing) Then
            mOnValueChanged.Invoke(Me, e)
        End If
        RaiseEvent ValueChanged(Me, e)

        Me.Invalidate()
    End Sub

    Protected Overridable Sub OnMaximumChanged(ByVal e As EventArgs)
        If Not (mOnMaximumChanged Is Nothing) Then
            mOnMaximumChanged.Invoke(Me, e)
        End If
        RaiseEvent MaximumChanged(Me, e)
        Me.Invalidate()

    End Sub
    Protected Overridable Sub OnMinimumChanged(ByVal e As EventArgs)
        If Not (mOnMinimumChanged Is Nothing) Then
            mOnMinimumChanged.Invoke(Me, e)
        End If
        RaiseEvent MinimumChanged(Me, e)
        Me.Invalidate()

    End Sub
    Protected Overridable Sub OnFaceTypeChanged(ByVal e As EventArgs)
        If Not (mOnFaceTypeChanged Is Nothing) Then
            mOnFaceTypeChanged.Invoke(Me, e)
        End If
        RaiseEvent FaceTypeChanged(Me, e)
        Me.Invalidate()

    End Sub
    Protected Overridable Sub OnRedLined(ByVal e As EventArgs)
        If Not (mOnRedLined Is Nothing) Then
            mOnRedLined.Invoke(Me, e)
        End If
        RaiseEvent RedLined(Me, e)
        _isRedlined = True
        Me.Invalidate()

    End Sub
    Protected Shadows Sub OnLostFocus(sender As Object, e As EventArgs)
        If Not (mOnLostFocus Is Nothing) Then
            mOnLostFocus.Invoke(Me, e)

        End If
        RaiseEvent LostFocus(Me, e)
        Me.Invalidate()
    End Sub
    Protected Shadows Sub OnGotFocus(sender As Object, e As EventArgs)
        If Not (mOnGotFocus Is Nothing) Then
            mOnGotFocus.Invoke(Me, e)

        End If
        RaiseEvent GotFocus(Me, e)
        Me.Invalidate()
    End Sub


#End Region

    Private Sub Unit_GotFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.GotFocus
        _hasfocus = True
        OnGotFocus(Me, EventArgs.Empty)
    End Sub

    Private Sub Unit_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress

    End Sub

    Private Sub Unit_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.LostFocus
        _hasfocus = False
        OnLostFocus(Me, EventArgs.Empty)
    End Sub

    'Protected Overrides Function ProcessMnemonic(ByVal charCode As Char) As Boolean
    '    Return MyBase.ProcessMnemonic(charCode)
    'End Function



    Private Sub Unit_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        'Paints the Control
        Dim f As Double
        f = ConvertValues2Degrees(_value)
        e.Graphics.SmoothingMode = SmoothingMode.HighQuality
        Select Case _faceType
            Case GaugeFaceType.White

                If alertLightLastState = True Then
                    e.Graphics.DrawImageUnscaled(My.Resources.map1a, 0, 0, 80, 80)
                Else

                    e.Graphics.DrawImageUnscaled(My.Resources.map1, 0, 0, 80, 80)
                End If

            Case GaugeFaceType.Black
                If alertLightLastState = True Then
                    e.Graphics.DrawImageUnscaled(My.Resources.map4a, 0, 0, 80, 80)
                Else

                    e.Graphics.DrawImageUnscaled(My.Resources.map4, 0, 0, 80, 80)
                End If
            Case GaugeFaceType.NewAge
                If alertLightLastState = True Then
                    e.Graphics.DrawImageUnscaled(My.Resources.map3a, 0, 0, 80, 80)
                Else

                    e.Graphics.DrawImageUnscaled(My.Resources.map3, 0, 0, 80, 80)
                End If


        End Select



        Dim gp As New GraphicsPath
        gp.AddLine(20, 20, 40, 40)
        Dim RotationTransform As New Matrix(1, 0, 0, 1, 1, 1)
        'Dim matrix As rotation
        Dim TheRotationPoint As New PointF(39.0F, 39.0F)
        'Dim point As rotation
        RotationTransform.RotateAt(f, TheRotationPoint)
        gp.Transform(RotationTransform)
        If _redline > 0 Then
            Dim penThickness As Integer

            If _redline Mod 2 = 1 Then
                penThickness = 6
            Else
                penThickness = 2
            End If
            If _redline > 2 Then
                penThickness *= 4
            End If
            Dim redPen As New Pen(Color.Red, penThickness)
            'Dim point1 As New Point(40, 12)
            'Dim point2 As New Point(57, 20)
            'Dim point3 As New Point(68, 40)

            'Dim curvePoints As Point() = {point1, point2, point3}
            Dim tmpRedLine As Integer = ConvertValues2Degrees(_redlineValue) - 140
            If _redline < 3 Then
                e.Graphics.DrawArc(redPen, 18, 18, 44, 44, tmpRedLine, -20 - tmpRedLine)
            Else
                e.Graphics.DrawArc(redPen, 22, 22, 40, 40, tmpRedLine, 10)
            End If
        End If
        Dim pn As New Pen(Color.Blue, 3)
        e.Graphics.DrawPath(pn, gp)
        If _hasfocus Then
            If _faceType = GaugeFaceType.NewAge Then
                Dim pnFocus As New Pen(Color.Blue, 1)
                e.Graphics.DrawRectangle(pnFocus, 0, 0, 79, 79)

            Else


                Dim pnFocus As New Pen(Color.White, 1)
                e.Graphics.DrawRectangle(pnFocus, 0, 0, 79, 79)
            End If
        End If

        lblValue.Text = Convert.ToInt32(_value).ToString
    End Sub


    Private Sub MoveDial()
        Dim f As Double
        Dim tempValue As Double = _value

        If Not Me.DesignMode Then
            If d <> 0 Then
                Select Case _accelerationMethod
                    Case MotionType.BellCurve
                        If d > 0 Then
                            If _value > dblHalfWay / 2 Then
                                _acceleration = _acceleration * 0.5
                            Else
                                _acceleration = _acceleration * 1.5
                            End If
                        Else
                            If _value < dblHalfWay / 2 Then
                                _acceleration = _acceleration * 1.5
                            Else
                                _acceleration = _acceleration * 0.5
                            End If
                        End If

                    Case MotionType.Decreasing
                        _acceleration = _acceleration * 0.5
                    Case MotionType.Increasing
                        _acceleration = _acceleration * 1.5
                    Case MotionType.SlowlyDecreasing
                        _acceleration = _acceleration * 0.9
                    Case MotionType.SlowlyIncreasing
                        _acceleration = _acceleration * 1.1

                End Select
                _velocity = _velocity + _acceleration



                Select Case d
                    Case Is > 0
                        If tempValue + _velocity < dblTarget Then
                            tempValue = tempValue + _velocity * d
                        Else
                            d = 0
                            tempValue = dblTarget

                        End If

                    Case 0

                    Case Is < 0
                        If tempValue - _velocity > dblTarget Then
                            tempValue = tempValue + (d * _velocity)
                        Else
                            d = 0
                            tempValue = dblTarget
                        End If

                End Select
                f = ConvertValues2Degrees(tempValue)
                If f > 120 Then
                    tempValue = _maximum
                    d = 0
                End If
                If f < -20 Then
                    tempValue = _minimum
                    d = 0
                End If
                Value = tempValue
            End If
            If _AlertLight = AlertLightStyle.AlertLightBlinking Then
                alertLightLastState = Not alertLightLastState
            End If

            Me.Invalidate()
            'counter += 1
        End If

    End Sub
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        MoveDial()

        'lbl_f.Text = String.Format("f: {0} am: {1}", Convert.ToInt32(f).ToString, Convert.ToInt32(_accelerationMethod).ToString)



    End Sub

    Private Sub Unit_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
        Select Case Me.BorderStyle
            Case Windows.Forms.BorderStyle.Fixed3D
                Me.Width = 84
                Me.Height = 84

            Case Windows.Forms.BorderStyle.FixedSingle
                Me.Width = 82
                Me.Height = 82

            Case Windows.Forms.BorderStyle.None
                Me.Width = 80
                Me.Height = 80

        End Select
        Me.Invalidate()


    End Sub
    Private Function ConvertDegrees2values(ByVal oldValue As Double) As Double
        Dim tmpFactor As Double
        If _maximum - _minimum <> 0 Then
            tmpFactor = (oldValue - _minimum) * (140 / _maximum - _minimum) + 20
        Else

        End If

        Return tmpFactor
    End Function

    Private Function ConvertValues2Degrees(ByVal Value2Convert As Double) As Double
        Dim tmpFactor As Double = _maximum - _minimum
        If tmpFactor <> 0 Then
            tmpFactor = (140 / tmpFactor)
        End If
        tmpFactor = (tmpFactor * (Value2Convert - _minimum)) - 20  '+ (_maximum - _minimum) / 2
        'lblValue.Text = tmpFactor.ToString


        Return tmpFactor


    End Function
    Public Sub AccelerateTo(ByVal NewValue As Double)
        If NewValue <> _value Then
            Timer1.Enabled = False
            If NewValue < _minimum Or NewValue > _maximum Then
                'raise error
            Else


                If NewValue < _value Then
                    d = -1
                    If _accelerationMethod = MotionType.BellCurve Then
                        dblHalfWay = _value - (_value - NewValue) / 2
                    End If
                Else
                    d = 1
                    If _accelerationMethod = MotionType.BellCurve Then
                        dblHalfWay = _value - (NewValue - _value) / 2
                    End If
                End If
                dblTarget = NewValue
                'f = ConvertValues2Degrees(_value)

                AccelControl()
                Timer1.Enabled = True
            End If

        End If
    End Sub

    Private Sub AccelControl()
        Select Case _accelerationMethod
            Case MotionType.SteadyAndAverage
                _velocity = 1
                _acceleration = 0
            Case MotionType.SteadyAndSlow
                _velocity = 0.2
                _acceleration = 0
            Case MotionType.SteadyAndFast
                _velocity = 5
                _acceleration = 0
            Case MotionType.BellCurve
                _velocity = 2
                _acceleration = 0.1

            Case MotionType.SlowlyIncreasing
                _velocity = 3
                _acceleration = 0.2
            Case MotionType.SlowlyDecreasing
                _velocity = 6
                _acceleration = -0.1
            Case MotionType.Increasing
                _velocity = 1
                _acceleration = 2
            Case MotionType.Decreasing
                _velocity = 14
                _acceleration = -0.66


        End Select
    End Sub

    Private Sub lblValue_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lblValue.Click
        Me.Focus()
        Me.OnClick(e)
    End Sub

    Private Sub lblValue_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles lblValue.DoubleClick
        Me.Focus()
        Me.OnDoubleClick(e)
    End Sub
End Class